#include "ArgParseur.h"

#include <iostream>
#include <iomanip>

using namespace std;

/**
 * Constructeur permettant d'initialiser la lecture des options.
 */
ArgParseur::ArgParseur( int argc, char* argv[] )
  : InitialisationFaite( false ), DemandeAide( false )
{
  interpreteArgs( argc, argv );
}

/**
 * Constructeur permettant d'initialiser la lecture des options et les
 * messages d'aides.
 */
ArgParseur::ArgParseur( int argc, char* argv[],
                      const map< string, string >& help )
  : InitialisationFaite( false ), DemandeAide( false ), TextesAide( help )
{
  interpreteArgs( argc, argv );
}

/**
 * Le desctructeur n'a rien de spécial à faire.
 */
ArgParseur::~ArgParseur()
{}

/**
 * Permet d'accéder au membre InitialisationFaite, qui indique si les
 * arguments on pu être lus correctement.
 *
 * @return une référence constante sur InitialisationFaite.
 */
const bool& ArgParseur::initialisationFaite() const
{
  return InitialisationFaite;
}

/**
 * Méthode responsable d'appeler la méthode de traitement des arguments, au cas où ceci n'a pas encore été fait.
 *
 * @param[in] argc nombre d'arguments.
 * @param[in] argv arguments.
 *
 * @return une référence sur InitialisationFaite.
 */
const bool& ArgParseur::interpreteArgs( int argc, char* argv[] )
{
  if ( initialisationFaite() )
    return InitialisationFaite;

  parseArgs( argc, argv );

  return InitialisationFaite;
}

/**
 * Permet d'accéder au membre DemandeAide, qui est \e true lorsque
 * l'option "-h" apparaît dans les arguments.
 *
 * @return une référence constante sur DemandeAide.
 */
const bool& ArgParseur::demandeAide() const
{
  return DemandeAide;
}

/**
 * Permet de déterminer les messages d'aide.
 *
 * @param[in] help conteneur associatif couplant l'option à sont texte
 * d'aide.
 */
void ArgParseur::setTextesAide( const map< string, string >& help )
{
  TextesAide = help;
}

/**
 * Permet de récupérer les options relatives à un certain marqueur.
 *
 * @param[in] key version courte du marqueur d'option.
 * @param[out] values conteneur des arguments lié au marqueur voulu.
 *
 * @return la taille du conteur \c values, ou -1 si l'option n'existe
 * pas.
 */
int ArgParseur::lireOption( const string& key,
                           vector< string >& values ) const
{
  values.clear();

  map< string, vector< string > >::const_iterator optIt;

  optIt = OptionsLues.find( key );

  if ( optIt == OptionsLues.end() )
    return -1;

  values.assign( optIt -> second . begin(), optIt -> second . end() );

  return values.size();
}

/**
 * Réalise l'affichage des messages d'aide dans la sortie standard. Si
 * les messages d'aide n'ont pas été définis, alors un message
 * d'erreur est affiché.
 *
 * @param[in] adjust permet d'aligner les messages d'aide lorsque vaut
 * \e true.
 */
void ArgParseur::montreAide( const bool& adjust ) const
{
  if ( TextesAide.size() == 0 )
  {
    cout << "Je ne peux pas vous aider, désolé!" << endl;
    return;
  }

  map< string, string >::const_iterator helpIt, helpBegin( TextesAide.begin() ),
    helpEnd( TextesAide.end() );

  cout << "Options disponibles : " << endl;

  size_t width( 0 );

  if ( adjust )
  {
    for ( helpIt = helpBegin; helpIt != helpEnd; helpIt++ )
      width = max( helpIt -> first . size(), width );
  }

  for ( helpIt = helpBegin; helpIt != helpEnd; helpIt++ )
    cout << "\t" << setw( width )<< left << helpIt -> first << " : "
         << helpIt -> second << endl;
}

/**
 * Permet d'afficher une instance de ArgParseur dans un flot de
 * sortie. Le résultat ressemble à :
 * 
 * tag1 :\n
 * option1\noption2\n
 * tag2 :\n
 * option1\noption2\n
 *
 * @param[in] os flot de sortie
 * @param[in] par instance d'ArgParseur.
 *
 * @return une référence sur le flot utilisé.
 */
ostream& operator<<( ostream& os, const ArgParseur& par )
{
  map< string, vector< string > >::const_iterator optIt,
    optBegin( par.OptionsLues.begin() ), optEnd( par.OptionsLues.end() );

  vector< string >::const_iterator vecIt, vecEnd;

  for ( optIt = optBegin; optIt != optEnd; optIt++ )
  {
    os << optIt -> first << " : " << endl << "\t";
    vecEnd = optIt -> second . end();
    for ( vecIt = optIt -> second . begin(); vecIt != vecEnd; vecIt++ )
      os << *vecIt << ' ';
    os << endl;
  }

  return os;
}

/**
 * Méthode responsable de l'interprétation de la ligne d'argument
 * donnés au programmes. Elle rempli le conteneur OptionsLues.
 *
 * @param[in] argc nombre d'arguments.
 * @param[in] argv arguments.
 */
void ArgParseur::parseArgs( int argc, char* argv[] )
{
  if ( InitialisationFaite )
    return;

  int i;

  string index( "" ), tmp;

  for ( i = 1; i < argc; i++ )
  {
    tmp = string( argv[ i ] );

      
    if ( tmp.substr( 0, 2 ) == "--" )
    {
      // if option is given by long-style option, the index is given by
      // the last '-' and the following character.
      index = tmp.substr( 1, 2 );

      if ( OptionsLues.find( index ) == OptionsLues.end() )
        OptionsLues[ index ] = vector< string >();

      continue;
    }
    else if ( tmp.substr( 0, 1 ) == "-" )
    {
      index = tmp;
      
      if ( OptionsLues.find( index ) == OptionsLues.end() )
        OptionsLues[ index ] = vector< string >();

      continue;
    }

    OptionsLues[ index ] . push_back( tmp );
  }

  if (  OptionsLues.find( "-h" ) != OptionsLues.end() )
    DemandeAide = true;

  InitialisationFaite = true;
}
